package br.ufsc.ctc.ine5622.compilador.semantico;


import java.util.LinkedList;
import java.util.List;

import br.ufsc.ctc.ine5622.compilador.lex.SemanticError;
import br.ufsc.ctc.ine5622.compilador.lex.Semantico;
import br.ufsc.ctc.ine5622.compilador.lex.Token;
import br.ufsc.ctc.ine5622.compilador.lex.Semantico.Categoria;
import br.ufsc.ctc.ine5622.compilador.lex.Semantico.TipoVariavel;
import br.ufsc.ctc.ine5622.compilador.semantico.Simbolo.ColunaKey;

/**
 * Classe que implementa todas as regras de semântica.
 * @author paulo
 *
 */
public class DeclarationAnalysis {

	/**
	 * Referência ao objeto principal da análise semantica.
	 */
	private Semantico semantico;
	
	// variaveis de declaração
	
	Semantico.ContextLid contextLid = null;      // contexto da lista de ids, pode ser declaração ou parametros formais
	int lidPosFirst = 0;                         // posição na TS do inicio da lista de ids
	int lidPosLast = 0;                          // posição na TS do fim da lista de ids
	Semantico.TipoVariavel tipoAtual = null;     // tipo da atual lista de ids 
	
	Semantico.TipoConstante tipoConst = null;    // marca o tipo de uma constante
	String valConst  = "nada";                   // marca o valor da constante
	int limiteInferiorDim = 0;                   // marca o valor do limite inferior da dimensao do vetor para comparação com o limite superior
	int limiteSuperiorDim = 0;                   // marca o valor do limite superior da dimensao do vetor
	int numeroDimensoes = 0;                     // marca o numero de dimensoes do vetor
	
	Semantico.MpPar mpPar = null;                // marca o método de passagem do parâmetro atual
	int numParFormais = 0;                       // número de marâmetros da função ou procedimento atual
	Simbolo procAtual = null;                    // função ou procedimento atual
	
	// variaveis de comandos 
	
	Simbolo idAtual = null;                      // id atual (em comandos)
	boolean opNega = false;                      // flag de operador de negação
	Semantico.TipoVariavel tipoFator = null;     
	boolean opUnitario = false;                  //
	
	Semantico.TipoVariavel tipoVar = null;
	Semantico.TipoVariavel tipoExpr = null;      // tipo da expressão
	Semantico.TipoVariavel tipoExprSimples = null;
	Semantico.TipoVariavel tipoTermo = null;
	Semantico.TipoVariavel tipoVarIndexavel;     // tipo da variavel de arranjo
	int numeroIndices = 0;                       // numero de indices para acesso a arranjo
	int npa = 0;                                 // número de parâmetros atuais
	
	Semantico.ContextExpr contextExpr = null;    //
	
	Semantico.TipoVariavel tipoLadoEsq = null;   // tipo do lado esquerdo da associação

	Simbolo varIndexavel = null;                 // guarda a variavel de arranjo na associação
	Simbolo varFuncao = null;                    // guarda a função da associação
	
	Semantico.Operador operador = null;          //
	
	public DeclarationAnalysis(Semantico sem){
		semantico = sem;
	}
	
	// regras de declaração

	//1
	public void programa(Token token) {
    	Simbolo atributo = new Simbolo(token.getLexeme(), Categoria.ID_PROGRAMA, token.getPosition());
    	atributo.getColunas().put(ColunaKey.NIVEL, semantico.getNivelAtual());
    	semantico.putOnTabelaSimbolos(atributo);
	}

	//2
	public void constId(Token token) throws SemanticError{
		Simbolo atributo = semantico.findAtributoByName(token.getLexeme()); 
		if(atributo != null){
			throw new SemanticError("ID '"+atributo.getNome()+"' já foi definido em "+atributo.getPosicao()+
					" como " + atributo.getCategoria(), atributo.getPosicao());
		}
		atributo = new Simbolo(token.getLexeme(), Categoria.CONSTANTE, token.getPosition());
	   	atributo.getColunas().put(ColunaKey.NIVEL, semantico.getNivelAtual());
		semantico.putOnTabelaSimbolos(atributo);
	}

	//3
	public void constVal(Token token) {
		Semantico.TipoVariavel tipoVar = null;
		if(tipoConst == Semantico.TipoConstante.INTEIRO){
			tipoVar = Semantico.TipoVariavel.INTEIRO;
		}else if(tipoConst == Semantico.TipoConstante.REAL){
			tipoVar = Semantico.TipoVariavel.REAL;
		}else if(tipoConst == Semantico.TipoConstante.LITERAL){
			tipoVar = Semantico.TipoVariavel.LITERAL;
		}else if(tipoConst == Semantico.TipoConstante.VERDADEIRO){
			tipoVar = Semantico.TipoVariavel.BOOLEANO;
		}else if(tipoConst == Semantico.TipoConstante.FALSO){
			tipoVar = Semantico.TipoVariavel.BOOLEANO;
		}
		semantico.getTabelaSimbolos().getLast().getColunas().put(ColunaKey.TIPO, tipoVar);
		semantico.getTabelaSimbolos().getLast().getColunas().put(ColunaKey.VALUE, valConst);
	}
	
	//4
	public void declLidInicio(Token token){
		contextLid = Semantico.ContextLid.DECL;
		lidPosFirst = semantico.getTabelaSimbolos().size();
	}
	
	//5
	public void declLidFim(Token token){
		lidPosLast = semantico.getTabelaSimbolos().size() -1;
	}
	
	//6
	public void varsTipo(Token token){
		for(int a = lidPosFirst; a <= lidPosLast; a++){
			semantico.setColuna(a, ColunaKey.TIPO, tipoAtual);
		}
		contextLid = null;
	}
	
	//7
	public void procId(Token token) throws SemanticError{
		Simbolo atributo = null;
		atributo = semantico.findAtributoByName(token.getLexeme());
		if(atributo != null){
			throw new SemanticError("ID '"+atributo.getNome()+"' já foi definido em "+atributo.getPosicao()+
					" como " + atributo.getCategoria(), atributo.getPosicao());
		}else{
			atributo = new Simbolo(token.getLexeme(), Categoria.PROCEDIMENTO, token.getPosition());
		   	atributo.getColunas().put(ColunaKey.NIVEL, semantico.getNivelAtual());
			semantico.putOnTabelaSimbolos(atributo);
			semantico.incrementaNivelAtual();
			numParFormais = 0;
			procAtual = atributo;
		}
	}
	
	//8
	public void funcaoId(Token token) throws SemanticError{
		Simbolo atributo = null;
		atributo = semantico.findAtributoByName(token.getLexeme());
		if(atributo != null){
			throw new SemanticError("ID '"+atributo.getNome()+"' já foi definido em "+atributo.getPosicao()+
					" como " + atributo.getCategoria(), atributo.getPosicao());
		}else{
			atributo = new Simbolo(token.getLexeme(), Categoria.FUNCAO, token.getPosition());
		   	atributo.getColunas().put(ColunaKey.NIVEL, semantico.getNivelAtual());
			semantico.putOnTabelaSimbolos(atributo);
			semantico.incrementaNivelAtual();
			numParFormais = 0;
			procAtual = atributo;
		}
	}
	
	//9
	public void setaNumParFormais(Token token){
		procAtual.getColunas().put(ColunaKey.NUM_PAR_FORMAIS, numParFormais);
	}
	
	//10
	public void setaTipoRetorno(Token token){
		procAtual.getColunas().put(ColunaKey.TIPO_RETORNO, tipoAtual);
	}
	
	//11
	public void cleanScope(Token token){
		semantico.removeNivel(semantico.getNivelAtual());
		semantico.decrementaNivelAtual();
	}
	
	//12
	public void parFormaisLidInicio(Token token){
		contextLid = Semantico.ContextLid.PAR_FORMAIS;
		lidPosFirst = semantico.getTabelaSimbolos().size();
	}
	
	//13
	public void parFormaisLidFim(Token token){
		lidPosLast = semantico.getTabelaSimbolos().size() -1;
	}
	
	//14
	@SuppressWarnings("unchecked")
	public void parFormaisTipo(Token token){
		Simbolo atr = null;
		List<Simbolo> listaParFormais = (List<Simbolo>)procAtual.getColunas().get(ColunaKey.LISTA_PAR_FORMAIS); 
		if(listaParFormais == null){
			listaParFormais = new LinkedList<Simbolo>();
		}
		procAtual.getColunas().put(ColunaKey.LISTA_PAR_FORMAIS, listaParFormais);
		
		for(int a = lidPosFirst; a <= lidPosLast; a++){
			token.getId();
			atr = semantico.getTabelaSimbolos().get(a);
			atr.getColunas().put(ColunaKey.TIPO, tipoAtual);
			atr.getColunas().put(ColunaKey.MP_PAR, mpPar);
			listaParFormais.add(atr);
		}
		contextLid = null;
		mpPar = null;
	}
	
	//15
	public void setMpParRef(Token token){
		mpPar = Semantico.MpPar.REFERENCIA;
	}
	
	//16
	public void setMpParVal(Token token){
		mpPar = Semantico.MpPar.VALOR;
	}
	
	//17
	public void id(Token token) throws SemanticError{
		Simbolo atributo = null;
		if(contextLid == Semantico.ContextLid.DECL){
			atributo = semantico.findAtributoByName(token.getLexeme());
			if(atributo != null){
				throw new SemanticError("ID '"+atributo.getNome()+"' já foi definido em "+atributo.getPosicao()+
						" como " + atributo.getCategoria(), atributo.getPosicao());
			}else{
				atributo = new Simbolo(token.getLexeme(), Categoria.VARIAVEL, token.getPosition());
			   	atributo.adicionaColuna(ColunaKey.NIVEL, semantico.getNivelAtual());
				semantico.putOnTabelaSimbolos(atributo);
			}
			return;
		}
		if(contextLid == Semantico.ContextLid.PAR_FORMAIS){
			atributo = semantico.findAtributoByName(token.getLexeme());
			if(atributo != null){
				throw new SemanticError("ID '"+atributo.getNome()+"' já foi definido em "+atributo.getPosicao()+
						" como " + atributo.getCategoria(), atributo.getPosicao());
			}else{
				atributo = new Simbolo(token.getLexeme(), Categoria.PARAMETRO, token.getPosition());
			   	atributo.adicionaColuna(ColunaKey.NIVEL, semantico.getNivelAtual());
				semantico.putOnTabelaSimbolos(atributo);
				numParFormais++;
			}
			return;
		}
	}
	
	//18
	public void cadeiaConstante(Token token) throws SemanticError{
		if(tipoConst != Semantico.TipoConstante.INTEIRO){
			throw new SemanticError("Token '"+token.getLexeme()+"' não é número inteiro, como se esperava.", token.getPosition());
		}else{
			int valor = Semantico.traduzInteiro(valConst);
			if(valor > 256 || valor <= 0){
				throw new SemanticError("Valor "+ valor + " não pertence a {x | 0 < x < 256}, como se esperava.", token.getPosition());
			}else{
				tipoAtual = Semantico.TipoVariavel.CADEIA;
				for(int a = lidPosFirst; a <= lidPosLast; a++){
					semantico.setColuna(a, ColunaKey.TAMANHO_CADEIA, valor);
					semantico.setColuna(a, ColunaKey.NUMERO_DIMENSOES, 1);
				}
			}
		}
	}
	
	//19
	public void vetorDimensao1(Token token){
		numeroDimensoes = 1;
		for(int a = lidPosFirst; a <= lidPosLast; a++){
			semantico.setColuna(a, ColunaKey.LIMITE_1_DIM_1, limiteInferiorDim);
		}
		for(int a = lidPosFirst; a <= lidPosLast; a++){
			semantico.setColuna(a, ColunaKey.LIMITE_2_DIM_1, limiteSuperiorDim);
		}
	}
	
	//20
	public void vetorSetDim2(Token token){
		if(numeroDimensoes == 2){
			for(int a = lidPosFirst; a <= lidPosLast; a++){
				semantico.setColuna(a, ColunaKey.LIMITE_1_DIM_2, limiteInferiorDim);
			}
			for(int a = lidPosFirst; a <= lidPosLast; a++){
				semantico.setColuna(a, ColunaKey.LIMITE_2_DIM_2, limiteSuperiorDim);
			}
		}
		for(int a = lidPosFirst; a <= lidPosLast; a++){
			semantico.setColuna(a, ColunaKey.NUMERO_DIMENSOES, numeroDimensoes);
		}
	}
	
	//21
	public void vetorTipo(Token token){
		Semantico.TipoVariavel tipo = null;
		if("inteiro".equals(token.getLexeme())){
			tipo = TipoVariavel.INTEIRO;
		}else if("real".equals(token.getLexeme())){
			tipo = TipoVariavel.REAL;
		}else if("literal".equals(token.getLexeme())){
			tipo = TipoVariavel.LITERAL;
		}else if("booleano".equals(token.getLexeme())){
			tipo = TipoVariavel.BOOLEANO;
		}
		tipoAtual = Semantico.TipoVariavel.VETOR;
		for(int a = lidPosFirst; a <= lidPosLast; a++){
			semantico.setColuna(a, ColunaKey.TIPO_ELEMENTOS, tipo);
		}
	}
	
	//22
	public void checaDimensaoLimite1(Token token) throws SemanticError{
		if(tipoConst != Semantico.TipoConstante.INTEIRO){
			throw new SemanticError("Token '"+token.getLexeme()+"' não é número inteiro, como se esperava.", token.getPosition());
		}		
		int val = Semantico.traduzInteiro(valConst);
		limiteInferiorDim = val;
	}
	
	//23
	public void checaDimensaoLimite2(Token token) throws SemanticError{
		if(tipoConst != Semantico.TipoConstante.INTEIRO){
			throw new SemanticError("Token '"+token.getLexeme()+"' não é número inteiro, como se esperava.", token.getPosition());
		}
		int val = Semantico.traduzInteiro(valConst);
		limiteSuperiorDim = val;
		if(val < limiteInferiorDim){
			throw new SemanticError("O limite superior do vetor, "+ val + ", não é maior que o limite inferior, "+limiteInferiorDim+".", token.getPosition());
		}
	}
	
	//24
	public void vetorDimensao2(Token token){
		numeroDimensoes = 2;
	}
	
	//25
	public void constanteId(Token token) throws SemanticError{
		Simbolo sim = semantico.findAtributoByName(token.getLexeme());
		if(sim == null){
			throw new SemanticError("ID '"+token.getLexeme()+"' não foi declarado.", token.getPosition());
		}else if(sim.getCategoria() != Semantico.Categoria.CONSTANTE){
			throw new SemanticError("ID '"+token.getLexeme()+"' não é do tipo constante.", token.getPosition());
		}else{
			Semantico.TipoVariavel tipo = (TipoVariavel) sim.getColunas().get(ColunaKey.TIPO);
			if(tipo == Semantico.TipoVariavel.INTEIRO){
				tipoConst = Semantico.TipoConstante.INTEIRO;
				valConst  = (String) sim.getColunas().get(ColunaKey.VALUE);
			}else if(tipo == Semantico.TipoVariavel.REAL){
				tipoConst = Semantico.TipoConstante.REAL;
				valConst  = (String) sim.getColunas().get(ColunaKey.VALUE);
			}else if(tipo == Semantico.TipoVariavel.LITERAL){
				tipoConst = Semantico.TipoConstante.LITERAL;
				valConst  = (String) sim.getColunas().get(ColunaKey.VALUE);
			}else if(tipo == Semantico.TipoVariavel.BOOLEANO){
				if("true".equals(valConst)){
					tipoConst = Semantico.TipoConstante.VERDADEIRO;
					valConst  = (String) sim.getColunas().get(ColunaKey.VALUE);	
				}else if("true".equals(valConst)){
					tipoConst = Semantico.TipoConstante.FALSO;
					valConst  = (String) sim.getColunas().get(ColunaKey.VALUE);
				} 
			}

		}
	}
	
	//26
	public void tipoInteiro(Token token){
		tipoAtual = Semantico.TipoVariavel.INTEIRO;
	}

	//27
	public void tipoReal(Token token){
		tipoAtual = Semantico.TipoVariavel.REAL;
	}
	
	//28
	public void tipoBooleano(Token token){
		tipoAtual = Semantico.TipoVariavel.BOOLEANO;
	}
	
	//29
	public void tipoCaracter(Token token){
		tipoAtual = Semantico.TipoVariavel.LITERAL;
	}
	
	// regras de comandos
	
	//30
	public void comandoId(Token token) throws SemanticError{
		Simbolo sim = semantico.findAtributoByName(token.getLexeme());
		if(sim == null){
			throw new SemanticError("ID '"+token.getLexeme()+"' não foi declarado.", token.getPosition());
		}else{
			idAtual = sim;
		}
	}
	
	//31
	public void checaTipoExpIntBool(Token token) throws SemanticError{
		if(tipoExpr != Semantico.TipoVariavel.INTEIRO && tipoExpr != Semantico.TipoVariavel.BOOLEANO){
			throw new SemanticError("Comando se rege inteiro ou booleano", token.getPosition());
		}
	}
	
	//32
	public void checaIdPara(Token token) throws SemanticError{
		Simbolo sim = semantico.findAtributoByName(token.getLexeme());
		if(sim == null){
			throw new SemanticError("Identificador "+token.getLexeme()+" não declarado", token.getPosition());
		}else {
			if(sim.getCategoria() != Semantico.Categoria.VARIAVEL && sim.getCategoria() != Semantico.Categoria.PARAMETRO) {
				throw new SemanticError("Comando para rege variável ou parâmetro do tipo inteiro", token.getPosition());
			}else if((Semantico.TipoVariavel)sim.getColunas().get(ColunaKey.TIPO) != Semantico.TipoVariavel.INTEIRO){
				throw new SemanticError("Comando para rege variável ou parâmetro do tipo inteiro", token.getPosition());
			}else{
				idAtual = sim;
			}
		}
	}
	
	//33
	public void checaTipoExprPara(Token token) throws SemanticError{
		if(tipoExpr != Semantico.TipoVariavel.INTEIRO){
			throw new SemanticError("Comando se rege expressão do tipo inteiro", token.getPosition());
		}
	}
	
	//34
	public void setaContextoLeitura(Token token){
		contextLid = Semantico.ContextLid.LEITURA;
	}
	
	//35
	public void setaContextExcrita(Token token) throws SemanticError{
		contextExpr = Semantico.ContextExpr.IMPRESSAO;
		if(tipoExpr == Semantico.TipoVariavel.VETOR || tipoExpr == Semantico.TipoVariavel.BOOLEANO){
			throw new SemanticError("Comando escreva rege expressão do tipo inteiro, real, literal ou cadeia", token.getPosition());
		}else{
			// gera codigo
		}
	}
	
	//36
	public void checaIdAssociacao(Token token)throws SemanticError{
		// idAtual vem da regra 30
		if(idAtual.getCategoria() == Semantico.Categoria.VARIAVEL || idAtual.getCategoria() == Semantico.Categoria.PARAMETRO){
			Semantico.TipoVariavel tipo = idAtual.getTipo();
			if(tipo == Semantico.TipoVariavel.VETOR || tipo == Semantico.TipoVariavel.CADEIA){
				throw new SemanticError("Id "+idAtual.getNome() +" não é arranjo", token.getPosition());
			}else{
				tipoLadoEsq = tipo;
			}
		}else if(idAtual.getCategoria() == Semantico.Categoria.FUNCAO) {
			if(procAtual != idAtual){
				throw new SemanticError("Associação ilegal fora do escopo da função " +idAtual.getNome(), token.getPosition());
			}else{
				tipoLadoEsq = (Semantico.TipoVariavel) idAtual.getColunas().get(ColunaKey.TIPO_RETORNO);
			}
		}else{
			throw new SemanticError("O lado esquerdo da associação deve ser variável, parâmetro ou função", token.getPosition());
		}
	}
	
	//37
	public void checaTipoLadoDireitoAssociacao(Token token)throws SemanticError{
		if(tipoExpr != tipoLadoEsq){
			throw new SemanticError("Laddo direito da expressão é de tipo diferente do lado esquerdo", token.getPosition());
		}else{
			//gera codigo
		}
	}
	
	//38
	public void checaIdAcessoArray(Token token)throws SemanticError{
		// idAtual vem da regra 30
		if(idAtual.getCategoria() != Semantico.Categoria.VARIAVEL){
			throw new SemanticError("Operador de acesso de arranjos só podem ser variáveis to tipo cadeia e vetor", token.getPosition());
		}else{
			Semantico.TipoVariavel tipo = idAtual.getTipo();
			if(tipo != Semantico.TipoVariavel.CADEIA && tipo != Semantico.TipoVariavel.VETOR){
				throw new SemanticError("Operador de acesso de arranjos só podem ser variáveis to tipo cadeia e vetor", token.getPosition());
			}else{
				tipoVarIndexavel = tipo;
				varIndexavel = idAtual;
			}
		}
	}
	
	//39
	public void checaIndicesArray(Token token)throws SemanticError{
		numeroIndices = 1;
		if(tipoVarIndexavel == Semantico.TipoVariavel.VETOR){
			Semantico.TipoVariavel tipo = varIndexavel.getTipoElementos();
			if(tipoExpr != Semantico.TipoVariavel.INTEIRO){
				throw new SemanticError("Tipo do indice deve ser inteiro", token.getPosition());
			}else{
				tipoLadoEsq = tipo;
			}
		}else{
			if(tipoExpr != Semantico.TipoVariavel.INTEIRO){
				throw new SemanticError("Tipo do indice deve ser inteiro", token.getPosition());
			}else{
				tipoLadoEsq = Semantico.TipoVariavel.LITERAL;
			}
		}
	}
	
	//40
	public void checaIndicesSegundosArray(Token token)throws SemanticError{
		if(numeroIndices == 2){
			if(tipoVarIndexavel == Semantico.TipoVariavel.CADEIA){
				throw new SemanticError("Cadeias só podem ter um índice", token.getPosition());
			}else{
				if(varIndexavel.getNumeroDimensoes() != 2){
					throw new SemanticError("Vetor unidimencional não pode ter dois índices", token.getPosition());
				}else{
					Semantico.TipoVariavel tipo = varIndexavel.getTipoElementos();
					if(tipoExpr != Semantico.TipoVariavel.INTEIRO){
						throw new SemanticError("Tipo do indice deve ser inteiro", token.getPosition());
					}else{
						tipoLadoEsq = tipo;
					}
				}
			}
		}else{
			if(varIndexavel.getNumeroDimensoes() == 2){
				throw new SemanticError("Vetor é bidimencional", token.getPosition());
			}
		}
	}
	
	//41
	public void checaIdChamadaFuncao(Token token) throws SemanticError{
		// idAtual vem da regra 30
		if(idAtual.getCategoria() != Semantico.Categoria.PROCEDIMENTO && idAtual.getCategoria() != Semantico.Categoria.FUNCAO){
			throw new SemanticError("id "+ idAtual.getNome()+ " deve ser função ou procedimento", token.getPosition());
		}
		varFuncao = idAtual;
	}
	
	//42
	public void checaParametrosFuncao(Token token) throws SemanticError{
		npa = 1;
		contextExpr = Semantico.ContextExpr.PAR_ATUAL;
		List<Simbolo> list = varFuncao.getListaParFormais();
		if(list != null){
			Simbolo arg = list.get(0);
			if(arg != null){
				if(arg.getTipo() != tipoVar){ 
					throw new SemanticError("Tipo do parâmetro errado, é "+tipoVar+", esperava-se "+arg.getTipo(), token.getPosition());
				}
			}
		}
		
	}
	
	//43
	public void checaNumeroParFormais(Token token) throws SemanticError{
		if(npa == varFuncao.getNumeroParFormais()){
			//gera codigo
		}else{
			throw new SemanticError("Numero de parametros formais não bate", token.getPosition());
		}
	}
	
	//44
	public void checaIdProcedure(Token token) throws SemanticError{
		if(idAtual.getCategoria() != Semantico.Categoria.FUNCAO && idAtual.getCategoria() != Semantico.Categoria.PROCEDIMENTO){
			throw new SemanticError(idAtual.getNome()+" não é procedimento ou função", token.getPosition());
		}else if((Integer)idAtual.getColunas().get(ColunaKey.NUM_PAR_FORMAIS) > 0){
			throw new SemanticError(idAtual.getNome() +" tem número de parâmetros maior que zero", token.getPosition());
		}else{
			//gera codigo
		}
	}
	
	//45
	public void expressao2(Token token){
		numeroIndices = 2;
	}
	
	//46
	public void repExpr(Token token) throws SemanticError{
		if(contextExpr == Semantico.ContextExpr.PAR_ATUAL){
			npa++;
			List<Simbolo> list = varFuncao.getListaParFormais();
			if(list != null){
				Simbolo arg = list.get(npa -1);
				if(arg != null){
					if(arg.getTipo() != tipoVar){
						throw new SemanticError("Tipo do parâmetro errado, é "+tipoVar+", esperava-se "+arg.getTipo(), token.getPosition());
					}
				}
			}
		}
		if(contextExpr == Semantico.ContextExpr.IMPRESSAO){
			if(tipoExpr == Semantico.TipoVariavel.BOOLEANO ||tipoExpr == Semantico.TipoVariavel.VETOR){
				throw new SemanticError("Tipo invalido de expressão", token.getPosition());
			}else{
				// gera codigo
			}
		}
	}
	
	//47
	public void setaTipoExpr(Token token){
		tipoExpr = tipoExprSimples;
	}
	
	//48
	public void exprSimplesRestoExpr(Token token) throws SemanticError{
		if(tipoExprSimples != tipoExpr){
			throw new SemanticError("Operandos incompativeis", token.getPosition());
		}else{
			tipoExpr = Semantico.TipoVariavel.BOOLEANO;
		}
	}
	
	//49
	public void setaIgual(Token token){
		operador = Semantico.Operador.IGUAL;
	}
	
	//50
	public void setaMenorQue(Token token){
		operador = Semantico.Operador.MENOR_QUE;
	}
	
	//51
	public void setaMaiorQue(Token token){
		operador = Semantico.Operador.MAIOR_QUE;
	}
	
	//52
	public void setaMaiorIgual(Token token){
		operador = Semantico.Operador.MAIOR_IGUAL;
	}
	
	//53
	public void setaMenorIgual(Token token){
		operador = Semantico.Operador.MENOR_IGUAL;
	}
	
	//54
	public void setaDiferente(Token token){
		operador = Semantico.Operador.DIFERENTE;
	}
	
	//55
	public void setaTipoExprSimples(Token token){
		tipoExprSimples = tipoTermo;
	}
	
	//56
	public void checaValidadeOperadorAdd(Token token) throws SemanticError{
		if(operador == Semantico.Operador.MAIS || operador == Semantico.Operador.MENOS){
			if(tipoExprSimples == Semantico.TipoVariavel.INTEIRO || tipoExprSimples == Semantico.TipoVariavel.REAL ||
					tipoExprSimples == Semantico.TipoVariavel.LITERAL){
				return;
			}
		}else if(operador == Semantico.Operador.ESTRELA || operador == Semantico.Operador.BARRA ||
				operador == Semantico.Operador.DIV){
			if(tipoExprSimples == Semantico.TipoVariavel.INTEIRO || tipoExprSimples == Semantico.TipoVariavel.REAL){
				return;
			}
		}else if(operador == Semantico.Operador.E || operador == Semantico.Operador.OU){
			if(tipoExprSimples == Semantico.TipoVariavel.BOOLEANO){
				return;
			}
		}else if(operador == Semantico.Operador.MAIOR_IGUAL ||
				operador == Semantico.Operador.MAIOR_QUE || operador == Semantico.Operador.MENOR_IGUAL ||
				operador == Semantico.Operador.MENOR_QUE){
			if(tipoExprSimples == Semantico.TipoVariavel.INTEIRO || tipoExprSimples == Semantico.TipoVariavel.REAL){
				return;
			}
		}else if(operador == Semantico.Operador.IGUAL || operador == Semantico.Operador.DIFERENTE){
			if(tipoExprSimples == Semantico.TipoVariavel.INTEIRO || tipoExprSimples == Semantico.TipoVariavel.REAL ||
					tipoExprSimples == Semantico.TipoVariavel.LITERAL || tipoExprSimples == Semantico.TipoVariavel.BOOLEANO){
				return;
			}
		}
		
		throw new SemanticError("Operador " +operador +" não aplicavel com este operando "+tipoExprSimples, token.getPosition());
	}
	
	//57
	public void checaCompatibilidadeTipoTermo(Token token) throws SemanticError{
		/* #57 - Se TipoTermo incompatível com TipoExpSimples
       então ERRO (“Operandos incompatíveis”)
	   senão TipoExpSimples := tipo do res. da operação
             (* G. Código de acordo com oppad *)  */
		if(tipoTermo == tipoExprSimples){
			return;
		}else{
			if((tipoTermo == Semantico.TipoVariavel.INTEIRO && tipoExprSimples == Semantico.TipoVariavel.REAL) ||
					(tipoTermo == Semantico.TipoVariavel.REAL && tipoExprSimples == Semantico.TipoVariavel.INTEIRO))
			{
				tipoExprSimples = Semantico.TipoVariavel.REAL;
			}
			if(    ((tipoTermo == Semantico.TipoVariavel.INTEIRO 
					 || tipoTermo == Semantico.TipoVariavel.REAL 
					 || tipoTermo == Semantico.TipoVariavel.BOOLEANO) 
					&& tipoExprSimples == Semantico.TipoVariavel.LITERAL) 
				||  
				    ((tipoExprSimples == Semantico.TipoVariavel.INTEIRO 
					 || tipoExprSimples == Semantico.TipoVariavel.REAL 
					 || tipoExprSimples == Semantico.TipoVariavel.BOOLEANO) 
					&& tipoTermo == Semantico.TipoVariavel.LITERAL))
			{
				tipoExprSimples = Semantico.TipoVariavel.LITERAL;
			}else{
				throw new SemanticError("Operandos incompatíveis: "+tipoTermo+" x "+tipoExprSimples, token.getPosition());
			}
		}
	}
	
	//58
	public void setaMais(Token token){
		operador = Semantico.Operador.MAIS;
	}
	
	//59
	public void setaMenos(Token token){
		operador = Semantico.Operador.MENOS;
	}
	
	//60
	public void setaOu(Token token){
		operador = Semantico.Operador.OU;
	}
	
	//61
	public void setaTipoTermo(Token token){
		tipoTermo = tipoFator;
	}
	
	//62
	public void checaAplicabilidaddeOperadorTipoTermo(Token token) throws SemanticError{
		if(operador == Semantico.Operador.MAIS || operador == Semantico.Operador.MENOS){
			if(tipoTermo == Semantico.TipoVariavel.INTEIRO || tipoTermo == Semantico.TipoVariavel.REAL ||
					tipoTermo == Semantico.TipoVariavel.LITERAL){
				return;
			}
		}else if(operador == Semantico.Operador.ESTRELA || operador == Semantico.Operador.BARRA ||
				operador == Semantico.Operador.DIV){
			if(tipoTermo == Semantico.TipoVariavel.INTEIRO || tipoTermo == Semantico.TipoVariavel.REAL){
				return;
			}
		}else if(operador == Semantico.Operador.E || operador == Semantico.Operador.OU){
			if(tipoTermo == Semantico.TipoVariavel.BOOLEANO){
				return;
			}
		}else if(operador == Semantico.Operador.MAIOR_IGUAL ||
				operador == Semantico.Operador.MAIOR_QUE || operador == Semantico.Operador.MENOR_IGUAL ||
				operador == Semantico.Operador.MENOR_QUE){
			if(tipoTermo == Semantico.TipoVariavel.INTEIRO || tipoTermo == Semantico.TipoVariavel.REAL){
				return;
			}
		}else if(operador == Semantico.Operador.IGUAL || operador == Semantico.Operador.DIFERENTE){
			if(tipoTermo == Semantico.TipoVariavel.INTEIRO || tipoTermo == Semantico.TipoVariavel.REAL ||
					tipoTermo == Semantico.TipoVariavel.LITERAL || tipoTermo == Semantico.TipoVariavel.BOOLEANO){
				return;
			}
		}
		
		throw new SemanticError("Operador " +operador +" não aplicavel com este operando "+tipoTermo, token.getPosition());
	}
	
	//63
	public void checaCompatibilidadeTipoFator(Token token) throws SemanticError{
		/*63 - Se TipoFator incompatível com TipoTermo
       então ERRO (“Operandos incompatíveis”)
	   senão TipoTermo := tipo do res. da operação
             (* G. Código de acordo com opmult *)  */
		if(tipoTermo == tipoFator){
			return;
		}else{
			if((tipoTermo == Semantico.TipoVariavel.INTEIRO && tipoFator == Semantico.TipoVariavel.REAL) ||
					(tipoTermo == Semantico.TipoVariavel.REAL && tipoFator == Semantico.TipoVariavel.INTEIRO))
			{
				tipoTermo = Semantico.TipoVariavel.REAL;
			}
			if(    ((tipoTermo == Semantico.TipoVariavel.INTEIRO 
					 || tipoTermo == Semantico.TipoVariavel.REAL 
					 || tipoTermo == Semantico.TipoVariavel.BOOLEANO) 
					&& tipoFator == Semantico.TipoVariavel.LITERAL) 
				||  
				    ((tipoFator == Semantico.TipoVariavel.INTEIRO 
					 || tipoFator == Semantico.TipoVariavel.REAL 
					 || tipoFator == Semantico.TipoVariavel.BOOLEANO) 
					&& tipoTermo == Semantico.TipoVariavel.LITERAL))
			{
				tipoTermo = Semantico.TipoVariavel.LITERAL;
			}else{
				throw new SemanticError("Operandos incompatíveis: "+tipoTermo+" x "+tipoFator, token.getPosition());
			}
		}
	}
	
	//64
	public void setaEstrela(Token token){
		operador = Semantico.Operador.ESTRELA;
	}
	
	//65
	public void setaBarra(Token token){
		operador = Semantico.Operador.BARRA;
	}
	
	//66
	public void setaDiv(Token token){
		operador = Semantico.Operador.DIV;
	}
	
	//67
	public void setaE(Token token){
		operador = Semantico.Operador.E;
	}

	//68
	public void checkNegacao(Token token) throws SemanticError{
		if(opNega){
			throw new SemanticError("Negações consecutivas são ilegais", token.getPosition());
		}else{
			opNega = true;
		}
	}
	
	//69
	public void checkTipoFatorBool(Token token) throws SemanticError{
		if(tipoFator != Semantico.TipoVariavel.BOOLEANO){
			throw new SemanticError("Negação rege operando booleano", token.getPosition());
		}
	}
	
	//70
	public void checkOpUnitario(Token token) throws SemanticError{
		if(opUnitario){
			throw new SemanticError("Operações unitárias não podem ser consecutivas", token.getPosition());
		}else{
			opUnitario = true;
		}
	}
	
	//71
	public void checkTipoFatorAdicao(Token token) throws SemanticError{
		if(tipoFator != Semantico.TipoVariavel.INTEIRO && tipoFator != Semantico.TipoVariavel.REAL){
			throw new SemanticError("Operadores de adição e subtração regem números inteiros ou reais", token.getPosition());
		}
	}
	
	//72
	public void resetaOperadoresFator(Token token){
		opUnitario = false;
		opNega = false;
	}
	
	//73
	public void setaTipoExpFator(Token token){
		tipoFator = tipoExpr;
	}
	
	//74
	public void setaTipoVarFator(Token token){
		tipoFator = tipoVar;
	}
	
	//75
	public void setaTipoCteFator(Token token){
		if(tipoConst == Semantico.TipoConstante.INTEIRO){
			tipoFator = Semantico.TipoVariavel.INTEIRO;
		}else if(tipoConst == Semantico.TipoConstante.REAL){
			tipoFator = Semantico.TipoVariavel.REAL;
		}else if(tipoConst == Semantico.TipoConstante.LITERAL){
			tipoFator = Semantico.TipoVariavel.LITERAL;
		}else if(tipoConst == Semantico.TipoConstante.VERDADEIRO){
			tipoFator = Semantico.TipoVariavel.BOOLEANO;
		}else if(tipoConst == Semantico.TipoConstante.FALSO){
			tipoFator = Semantico.TipoVariavel.BOOLEANO;
		}
	}
	
	//76
	public void checaCategoriaIdFuncao(Token token) throws SemanticError{
		if(idAtual.getCategoria() != Semantico.Categoria.FUNCAO && idAtual.getCategoria() != Semantico.Categoria.PROCEDIMENTO){
			throw new SemanticError(idAtual.getNome() +" não é função ou procedimento", token.getPosition());
		}
		varFuncao = idAtual;
	}
	
	//77
	public void checaNpaNpf(Token token){
		if(npa == varFuncao.getNumeroParFormais()){
			tipoVar = varFuncao.getTipoRetorno();
			// gera codigo da chamada de funcao
		}
	}
	
	//78
	public void regra78(Token token) throws SemanticError{
		numeroIndices = 1;
		if(tipoVarIndexavel == Semantico.TipoVariavel.VETOR){
			if(tipoExpr != Semantico.TipoVariavel.INTEIRO){
				throw new SemanticError("Tipo do indice inválido", token.getPosition());
			}else{
				tipoVar = varIndexavel.getTipoElementos();
			}
		}else{
			if(tipoExpr != Semantico.TipoVariavel.INTEIRO){
				throw new SemanticError("Tipo do indice inválido", token.getPosition());
			}else{
				tipoVar = Semantico.TipoVariavel.LITERAL;
			}
		}
	}
	
	//79
	public void regra79(Token token) throws SemanticError{
		if(numeroIndices == 2){
			if(tipoVarIndexavel == Semantico.TipoVariavel.CADEIA){
				throw new SemanticError("Cadeias só podem ter um índice", token.getPosition());
			}else{
				if(varIndexavel.getNumeroDimensoes() != 2){
					throw new SemanticError("Vetor é unidimensional", token.getPosition());
				}else{
					tipoVar = varIndexavel.getTipoElementos();
				}
			}
		}else{
			if(varIndexavel.getNumeroDimensoes() == 2){
				throw new SemanticError("Vetor é bidimensional", token.getPosition());
			}
		}
	}
	
	//80
	public void regra80(Token token) throws SemanticError{
		if(idAtual.getCategoria() == Semantico.Categoria.VARIAVEL || idAtual.getCategoria() == Semantico.Categoria.PARAMETRO){
			if(idAtual.getTipo() == Semantico.TipoVariavel.VETOR){
				throw new SemanticError("Vetor deve ser indexado", token.getPosition());
			}else{
				tipoVar = idAtual.getTipo();
			}
		}else if(idAtual.getCategoria() == Semantico.Categoria.FUNCAO){
			if(idAtual.getNumeroParFormais() != 0){
				throw new SemanticError("Erro na quantidade de parametros formais", token.getPosition());
			}else{
				// gera codigo
				tipoVar = varFuncao.getTipoRetorno();
			}
		}else if(idAtual.getCategoria() == Semantico.Categoria.CONSTANTE){
			tipoVar = idAtual.getTipo();
		}else{
			throw new SemanticError("Esperava-se variável, função ou constante", token.getPosition());
		}
	}
	
	//81
	public void setaConstanteInteiro(Token token){
		tipoConst = Semantico.TipoConstante.INTEIRO;
		valConst  = token.getLexeme();
	}
	
	//82
	public void setaConstanteReal(Token token){
		tipoConst = Semantico.TipoConstante.REAL;
		valConst  = token.getLexeme();
	}
	
	//83
	public void setaConstanteVerdadeiro(Token token){
		tipoConst = Semantico.TipoConstante.VERDADEIRO;
		valConst  = token.getLexeme();
	}
	
	//84
	public void setaConstanteFalso(Token token){
		tipoConst = Semantico.TipoConstante.FALSO;
		valConst  = token.getLexeme();
	}
	
	//85
	public void setaConstanteLiteral(Token token){
		tipoConst = Semantico.TipoConstante.LITERAL;
		valConst  = token.getLexeme();
	}

}
